﻿using System;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;

namespace XEvolve.Presentation.Data
{
    public class BinaryValueConverter<SourceTypeFoo, SourceTypeBar, TargetType> : IMultiValueConverter
    {
        private readonly Func<SourceTypeFoo, SourceTypeBar, object, TargetType> convertToFunc;
        private readonly Func<TargetType, object, object[]> convertBackFunc;

        public BinaryValueConverter(Func<SourceTypeFoo, SourceTypeBar, object, TargetType> convertToFunc, Func<TargetType, object, object[]> convertBackFunc)
        {
            this.convertToFunc = convertToFunc;
            this.convertBackFunc = convertBackFunc;
        }

        public BinaryValueConverter(Func<SourceTypeFoo, SourceTypeBar, TargetType> convertToFunc)
            : this((sourceFoo, sourceBar, parameter) => convertToFunc(sourceFoo, sourceBar), null)
        {
        }

        public BinaryValueConverter(Func<SourceTypeFoo, SourceTypeBar, object, TargetType> convertToFunc)
            : this(convertToFunc, null)
        {
        }

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values.Any(v => v == DependencyProperty.UnsetValue)) return null;
            var parameterFoo = typeof(SourceTypeFoo).IsArray ? AsType((object[])values[0], typeof(SourceTypeFoo).GetElementType()) : (SourceTypeFoo)values[0];
            var parameterBar = typeof(SourceTypeBar).IsArray ? AsType((object[])values[1], typeof(SourceTypeBar).GetElementType()) : (SourceTypeBar)values[1];
            return convertToFunc((SourceTypeFoo) parameterFoo, (SourceTypeBar) parameterBar, parameter);
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return convertBackFunc((TargetType)value, parameter);
        }

        internal static object AsType(object[] values, Type elementType)
        {
            var parameterValue = Array.CreateInstance(elementType, values.Length);
            values.CopyTo(parameterValue, 0);
            return parameterValue;
        }
    }
}
